/*
 * tcp_socketServer_tsk.c
 *
 *  Created on: Oct 25, 2021
 *      Author: zchaojian
 */

#include "tcp_socketServer_tsk.h"
#include "blink.h"
#include "esp_log.h"
#include "eth_connect.h"
#include "lwip/sockets.h"
#include "native_ota.h"
#include "tcpSocket_tsk.h"
#include "udp_brdcast.h"
#include "wifi_connect.h"

#include "config_app.h"
#include "download_tcp_server.h"
#include "ingwaycomm.h"
#include "modbus_svc.h"
#include "tcp_socketClient_tsk.h"

#define TCP_SERVER_SOCKET_PORT 502
#define MAX_NUM_CLIENT 5
#define LISTEN_NUM 1

uint8_t mTcpServerListened = 0;
uint8_t mTcpServerConnected = 0;
uint8_t start_myHTTP = 0;
static int mServerSocket = 0;
// other client socket with can link remote tcp server
static int mClientSocket = 0;

static fd_set fdAllSockets;
static int iFdMax, iFdMin;
static int iFdClientMin;
static int mAddrFamily;

extern uint16_t Zhong_Xin_Login;
extern uint16_t Zhong_xin_Switch;

static const char* TAG = "TCP_SocketServer";

static void vTCP_SocketServer_Init(void)
{
    static struct sockaddr_in6 tDestAddr;
    static struct sockaddr_in* tDestAddrIp4 = (struct sockaddr_in*)&tDestAddr;
    uint16_t uiTcpServerPort;
    uiTcpServerPort = 0;
    mAddrFamily = AF_INET;
    if (mAddrFamily == AF_INET) {
        tDestAddrIp4->sin_addr.s_addr = htonl(0);
        tDestAddrIp4->sin_family = AF_INET;
        if (tSystemFucntion.ucEthEnable == FUNCTION_WORK) {
            tDestAddrIp4->sin_port = htons(/*TCP_SERVER_SOCKET_PORT*/ mPartitionTable.tEthernetConfig.EthernetPort);
            uiTcpServerPort = mPartitionTable.tEthernetConfig.EthernetPort;
        } else if (tSystemFucntion.ucWifiEnable == FUNCTION_WORK) {
            tDestAddrIp4->sin_port = htons(/*TCP_SERVER_SOCKET_PORT*/ mPartitionTable.tWifiConfig.usStaPort);
            uiTcpServerPort = mPartitionTable.tWifiConfig.usStaPort;
        }

    } else if (mAddrFamily == AF_INET6) {
        bzero(&tDestAddr.sin6_addr.un, sizeof(tDestAddr.sin6_addr.un));
        tDestAddr.sin6_family = AF_INET6;
        tDestAddr.sin6_port = htons(mPartitionTable.tEthernetConfig.EthernetPort);
    }
    mServerSocket = socket(mAddrFamily, SOCK_STREAM, mAddrFamily == AF_INET6 ? IPPROTO_IPV6 : IPPROTO_IP);
    if (mServerSocket < 0) {
        ESP_LOGE(TAG, "Ethernet socket server creat fail!");
        shutdown(mServerSocket, SHUT_RDWR);
        close(mServerSocket);
        return;
    }
    ESP_LOGI(TAG, "Ethernet socket server created! fd:%d", mServerSocket);

    // the port of tcp server reuse
    int iReuseAddrFlag = 1;
    int iErr = setsockopt(mServerSocket, SOL_SOCKET, SO_REUSEADDR, &iReuseAddrFlag, sizeof(iReuseAddrFlag));
    if (iErr < 0) {
        ESP_LOGE(TAG, "Ethernet socket server reuse addr fiald");
        shutdown(mServerSocket, SHUT_RDWR);
        close(mServerSocket);
        return;
    }
    iErr = bind(mServerSocket, (struct sockaddr*)&tDestAddr, sizeof(tDestAddr));
    if (iErr != 0) {
        ESP_LOGE(TAG, "Ethernet socket server bind creat fail!");
        shutdown(mServerSocket, SHUT_RDWR);
        close(mServerSocket);
        return;
    }
    ESP_LOGI(TAG, "Ethernet socket server bind created! Port:%d", uiTcpServerPort);
    iErr = listen(mServerSocket, LISTEN_NUM);
    if (iErr != 0) {
        mTcpServerListened = 0;
        ESP_LOGE(TAG, "Ethernet socket server listened fail!");
        shutdown(mServerSocket, SHUT_RDWR);
        close(mServerSocket);
        return;
    }
    ESP_LOGI(TAG, "Ethernet socket server listened!");
    mTcpServerListened = 1;
    // xEventGroupSetBits(eETHTaskOrderEventGroup, ETH_SERVERLISTENED_BIT);

    // clear allSockets
    FD_ZERO(&fdAllSockets);
    // register tcp server fd in allSockets
    FD_SET(mServerSocket, &fdAllSockets);
    for (uint8_t i = 0; i < 20; i++) {
        if (connect_record[i] == 0) {
            connect_record[i] = mServerSocket;
            break;
        }
    }
    J_login_manage[mServerSocket].connect_flag = 0xCD;
    J_login_manage[mServerSocket].connect_Login |= Zhong_xin_Switch << 4;

    iFdMax = iFdMax > mServerSocket ? iFdMax : mServerSocket;
    iFdMin = iFdMin > mServerSocket ? mServerSocket : iFdMin;
    iFdClientMin = mServerSocket + MAX_NUM_CLIENT * 2;
}

extern int chint_web_stop(void);
extern int chint_web_init(void);
static void vSelectHandleSockets(int iFd)
{
    int iLen;
    char cAddrStr[32] = { 0 };
    uint16_t connectPort = 0;
    char cRxBuffer[CONFIG_SOCKET_FRAME_SIZE];
    ModbusRtuDataTPDF* mModbusRtuDataRec = NULL;
    struct sockaddr_in6 tSourceAddr; // Large enough for both IPv4 or IPv6

    uint iAddrLen = sizeof(tSourceAddr);
    mModbusRtuDataRec = pModbusRtuDataRec();

    if (iFd == mServerSocket) {
        int iClientSocket = accept(mServerSocket, (struct sockaddr*)&tSourceAddr, &iAddrLen);
        if (iClientSocket < 0) {
            ESP_LOGE(TAG, "Ethernet socket server accecpt fail!  errno %d", errno);
            // close tcp server
            //  chint_web_stop();
            shutdown(mServerSocket, SHUT_RDWR);
            close(mServerSocket);
        } else {
            // get client min fd
            if (iFdClientMin > iClientSocket) {
                iFdClientMin = iClientSocket;
            }
            if (mTcpServerConnected < 255) {
                mTcpServerConnected = mTcpServerConnected + 1;
            }
            // control connect client counter
            if (mTcpServerConnected > MAX_NUM_CLIENT) {
                ESP_LOGE(TAG, "Accept client is full! Client fd : %d", iClientSocket);
                close(iClientSocket);
                mTcpServerConnected = mTcpServerConnected > 0 ? mTcpServerConnected - 1 : 0;
                ESP_LOGI(TAG, "1---There are %d client in...", mTcpServerConnected);
                return;
            }
            iFdMax = iFdMax > iClientSocket ? iFdMax : iClientSocket;
            iFdMin = iFdMin > iClientSocket ? iClientSocket : iFdMin;

            // if (iFdMax < iClientSocket)
            // {
            //     iFdMax = iClientSocket;
            // }
            FD_SET(iClientSocket, &fdAllSockets); // add client socket to AllSockets
            for (uint8_t i = 0; i < 20; i++) {
                if (connect_record[i] == 0) {
                    connect_record[i] = iClientSocket;
                    break;
                }
            }
            J_login_manage[iClientSocket].connect_flag = 0xCD; // 记录单个连接状态，及单个登录状态
            J_login_manage[iClientSocket].connect_Login |= Zhong_xin_Switch << 4;
            // Convert ip address to string
            if (tSourceAddr.sin6_family == AF_INET) {
                inet_ntoa_r(((struct sockaddr_in*)&tSourceAddr)->sin_addr.s_addr, cAddrStr, sizeof(cAddrStr) - 1);
            } else if (tSourceAddr.sin6_family == AF_INET6) {
                inet6_ntoa_r(tSourceAddr.sin6_addr, cAddrStr, sizeof(cAddrStr) - 1);
            }
            connectPort = ntohs(tSourceAddr.sin6_port);
            ESP_LOGI(TAG, "Ethernet socket server client in, ip address: %s,Port:%d Client fd:%d", cAddrStr, connectPort, iClientSocket);
            ESP_LOGI(TAG, "2---There are %d client in...", mTcpServerConnected);
            if (mTcpServerConnected < 8) {
                for (int i = 0; i < 8; i++) {
                    if (tcp_info_buff[i].port == 0 && tcp_info_buff[i].role == 0) {
                        memset(tcp_info_buff[i].IP, 0, 32);
                        memcpy(tcp_info_buff[i].IP, cAddrStr, 32);
                        tcp_info_buff[i].connect_fd = iClientSocket;
                        tcp_info_buff[i].port = connectPort;
                        tcp_info_buff[i].role = REMOTE_CLIENT;
                        break;
                    }
                }
            }

            // 设置每个client的保活参数
            set_keepalive(iClientSocket, KEEP_IDLE, KEEP_INTERVAL, KEEP_PROBE_TIMES);
            // gpio_set_level(IOT_LED, LED_ON);
            if (mTcpServerConnected == MAX_NUM_CLIENT) {
                ESP_LOGI(TAG, "16 set 0");
            } else {
            }
        }
    } else {
        iLen = recv(iFd, cRxBuffer, sizeof(cRxBuffer) - 1, 0);
        if (iLen < 0) {
            ESP_LOGE(TAG, "Ethernet socket server data recv() < 0! iFd:%d", iFd);
            FD_CLR(iFd, &fdAllSockets); // client down, and clear client fd from fdAllSockets
            shutdown(iFd, 0);
            close(iFd);

            for (uint8_t i = 0; i < 20; i++) {
                if (connect_record[i] == iFd) {
                    J_login_manage[iFd].connect_flag = 0;
                    J_login_manage[iFd].connect_Login = 0;
                    connect_record[i] = 0;
                    break;
                }
            }
            printf("iFd = %d,tcp_info_buff[mTcpServerConnected].connect_fd = %d\n", iFd, tcp_info_buff[mTcpServerConnected].connect_fd);
            clear_tcp_info(iFd);
            mTcpServerConnected = mTcpServerConnected > 0 ? mTcpServerConnected - 1 : 0;
            ESP_LOGI(TAG, "3---There are %d client in...", mTcpServerConnected);
            if (mTcpServerConnected == 0) {
                gpio_set_level(IOT_LED, LED_OFF);
            }
            if (mTcpServerConnected < MAX_NUM_CLIENT) {
            }
        } else if (iLen == 0) {
            ESP_LOGE(TAG, "Ethernet socket server client down! recv() = 0 iFd:%d", iFd);
            for (uint8_t i = 0; i < 20; i++) {
                if (connect_record[i] == iFd) {
                    J_login_manage[iFd].connect_flag = 0;
                    J_login_manage[iFd].connect_Login = 0;
                    connect_record[i] = 0;
                    break;
                }
            }
            ESP_LOGE(TAG, "Login = %x", Zhong_Xin_Login);
            FD_CLR(iFd, &fdAllSockets); // client down, and clear client fd from fdAllSockets
            shutdown(iFd, 0);
            close(iFd);
            if (iFd == mClientSocket) {
                ESP_LOGE(TAG, "Local tcp client ERROR!!! iFd:%d", iFd);
                vChangeSocketClientConnectValue(0);
            } else {
                clear_tcp_info(iFd);
                mTcpServerConnected = mTcpServerConnected > 0 ? mTcpServerConnected - 1 : 0;
                ESP_LOGI(TAG, "4---There are %d client in...", mTcpServerConnected);
                if (mTcpServerConnected == 0) {
                    gpio_set_level(IOT_LED, LED_OFF);
                }
                if (mTcpServerConnected < MAX_NUM_CLIENT) {
                }
            }
        } else {

            cRxBuffer[iLen] = 0;
            if (ANA_LOG) {
                ESP_LOGW(TAG, "---------------new server data---------------Len: %d", iLen);
                esp_log_buffer_hex(TAG, cRxBuffer, iLen);
            }

            xSemaphoreTake(ModbusTCP_handle_sem, pdMS_TO_TICKS(5000));
            MODB_vAnalysis_TCP((uint8_t*)cRxBuffer, iLen, mModbusRtuDataRec);
            xSemaphoreGive(ModbusTCP_handle_sem);

            mModbusRtuDataRec->tTag = (void*)iFd;
            if (ANA_LOG) {
                ESP_LOGI(TAG, "Analysis mModbusRtuDataRec->tTag:%d", (int)mModbusRtuDataRec->tTag);
            }
        }
    }
}

extern uint8_t MEM_LOG;
/*tcp server task start*/
static void vTcp_Server_Task(void* pvParameters)
{
    uint8_t mConnectedLocal;
    uint8_t ucOTATcpServerListened;
    int iFdResult;
    struct timeval tSelectTime;
    // CPU复位原因寄存器地址
    volatile uint32_t* reset_reg = 0x3ff48000 + 0x34;
    tSelectTime.tv_sec = 5;
    tSelectTime.tv_usec = 0;
    while (1) {
        fd_set fdReadSockets = fdAllSockets;
        fd_set fdWriteSockets = fdAllSockets;
        fd_set fdErrorSockets = fdAllSockets;
        mConnectedLocal = Eth_Connected_Status() | Wifi_Connected_Status();
        // ESP_LOGI(TAG, "mConnectedLocal: %d", mConnectedLocal);
        if (mConnectedLocal) {
            // ucOTATcpServerListened = uOTA_TcpServerListened();
            // if(ucOTATcpServerListened != 1)
            // {
            // 	ESP_LOGE(TAG, "OTA TCP server don't listend");
            // 	vTaskDelay(200);
            // 	continue;
            // }

            if (!mTcpServerListened) {
                vTCP_SocketServer_Init();
                start_myHTTP = 1;
                ESP_LOGE(TAG, "SocketServer_Init %d", start_myHTTP);
            } else {
                // select
                iFdResult = select(iFdMax + 1, &fdReadSockets, NULL, &fdErrorSockets, &tSelectTime);

                if (iFdResult < 0) {
                    ESP_LOGE(TAG, "Select() return:%d", iFdResult);
                    // chint_web_stop();
                    mTcpServerListened = 0;
                    mTcpServerConnected = 0;
                    // close tcp client
                    for (int i = mServerSocket; i < 64; i++) {
                        shutdown(i, SHUT_RDWR);
                        close(i);
                    }
                    for (uint8_t i = 0; i < 20; i++) {
                        J_login_manage[connect_record[i]].connect_flag = 0;
                        J_login_manage[connect_record[i]].connect_Login = 0;
                        connect_record[i] = 0;
                    }
                    // close tcp server
                    shutdown(mServerSocket, SHUT_RDWR);
                    close(mServerSocket);
                    ESP_LOGE(TAG, "mTcpServerListened:%d", mTcpServerListened);
                    ESP_LOGE(TAG, "mTcpServerConnected:%d", mTcpServerConnected);
                    if (mTcpServerConnected < MAX_NUM_CLIENT) {
                        ESP_LOGI(TAG, "16 set 1");
                    }
                    if (mTcpServerConnected == 0) {
                        gpio_set_level(IOT_LED, LED_OFF);
                        ESP_LOGI(TAG, "4 set 1");
                    }
                    FD_ZERO(&fdAllSockets);
                    // register tcp server fd in allSockets
                    FD_SET(mServerSocket, &fdAllSockets);
                    // J_login_manage[mServerSocket].connect_flag = 0;           //记录单个连接状态，及单个登录状态

                    vTaskDelay(3 * 1000 / portTICK_PERIOD_MS);
                    continue;
                } else if (0 == iFdResult) // 没有响应的socket
                {
                    if (MEM_LOG || ANA_LOG) {
                        // esp32-S3 reset reason ESP_LOGW(TAG, "esp_reset_reason() = 0x%02x,RTC_CNTL_RESET_STATE_REG value = 0x%02x,RTC_CNTL_RESET_CAUSE_APPCPU = 0x%02x,RTC_CNTL_RESET_CAUSE_PROCPU = 0x%02x", esp_reset_reason(), *reset_reg, (*reset_reg >> 6) & 0x3F, *reset_reg & 0x3F);
                        ESP_LOGW(TAG, "esp_reset_reason() = 0x%02x,RTC_CNTL_RESET_STATE_REG value = 0x%02x,RTC_CNTL_RESET_CAUSE_APPCPU = 0x%02x,RTC_CNTL_RESET_CAUSE_PROCPU = 0x%02x", esp_reset_reason(), *reset_reg, (*reset_reg >> 6) & 0x3F, *reset_reg & 0x3F);
                        ESP_LOGI(TAG, "Free memory: on chip = %.2f Kbytes,total = %.2f Kbytes", heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_DMA) / 1024.0, esp_get_free_heap_size() / 1024.0);
                        printf("Free memory: on chip = %.2f Kbytes,total = %.2f Kbytes\n", heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_DMA) / 1024.0, esp_get_free_heap_size() / 1024.0);
                    }
                    ESP_LOGW(TAG, "Wait all sockets change... time out! Select time:%ds",
                        (int)tSelectTime.tv_sec);
                    continue;
                } else if (iFdResult > 0) {
                    if (ANA_LOG) {
                        ESP_LOGE("TAG", "-----------------------iFdResult = %d ", iFdResult);
                    }

                    // handle error sockets

                    // handle writed sockets

                    // handle read sockets      mServerSocket + MAX_NUM_CLIENT
                    for (int i = iFdMin; i < iFdMax + 1; i++) {
                        // ESP_LOGI(TAG, "fd:%d", fd);
                        if (FD_ISSET(i, &fdReadSockets)) {
                            vSelectHandleSockets(i);
                        }
                    }
                }
            }
        }
        vTaskDelay(2);
    }
}

/*tcp socket  server task initialize*/
void vTcpServerTask_vInit(void)
{
    xTaskCreate(vTcp_Server_Task, "socket server task", 4096, NULL, 4, NULL);
    Udp_Brdcast_Start();
    // Native_Ota_Start();
}

/* Use function return tcp socket server establish status */
uint8_t uTcp_SocketServerListened(void)
{
    return mTcpServerListened;
}

/* Use function return tcp socket server establish status */
uint8_t uTcp_SocketServerConnected(void)
{
    return mTcpServerConnected;
}

/*tcp socker server close*/
void vTcp_SocketServer_Close(void)
{
    if (mTcpServerListened) {
        shutdown(mServerSocket, SHUT_RDWR);
        int s = close(mServerSocket);
        if (s == 0) {
            mTcpServerListened = 0;
            for (int i = mServerSocket + 1; i < 64; i++) {
                shutdown(i, SHUT_RDWR);
                close(i);
            }
            for (uint8_t i = 0; i < 20; i++) {
                J_login_manage[connect_record[i]].connect_flag = 0;
                J_login_manage[connect_record[i]].connect_Login = 0;
                connect_record[i] = 0;
            }
        }
        // ESP_LOGW("TAG","vTcp_SocketServer_Close = %d,%d",s,mServerSocket);
    }
}

/*add a new tcp socket client fd to fdAllSockets*/
void vAddClientFdToAllSockets(int iTcplientFd)
{
    mClientSocket = iTcplientFd;
    // get fd max to confirm for() loop times
    //  if (iFdMax < iTcplientFd)
    // {
    //     iFdMax = iTcplientFd;
    // }
    iFdMax = iFdMax > iTcplientFd ? iFdMax : iTcplientFd;
    iFdMin = iFdMin > iTcplientFd ? iTcplientFd : iFdMin;
    FD_SET(iTcplientFd, &fdAllSockets); // add tcp client socket to AllSockets
    for (uint8_t i = 0; i < 20; i++) {
        if (connect_record[i] == 0) {
            connect_record[i] = iTcplientFd;
            break;
        }
    }
    J_login_manage[iTcplientFd].connect_flag = 0xCD; // 记录单个连接状态，及单个登录状态
    J_login_manage[iTcplientFd].connect_Login |= Zhong_xin_Switch << 4;
}
